﻿/*
 * HandBrake Batch Encoder Mk II 
 * v1.01 5/13/10 
 * Joe H. joeh100@hotmail.com
 * 
 * v1.01 5/13/10 
 * S Hawkes 
 * 
 *    This Program was adapted from an origianl project downloaded from codeplex by the name of "Handbrake Batch Encoder"
 * (http://handbrakebatchencode.codeplex.com), and authored by the username "buzzby".  After attempting to contact the 
 * original author to submit code changes, and being unable to do so, I assume that the origianl project is dead.
 *    Because of this I have started a new project on codeplex for this new project "Handbrake Batch Encoder Mk II".
 * I am not a programmer, just a hobbyist who has done some basic programming in the past and needed this program 
 *    for myself and wanted to see the project advance.
 *
 *    The following changes have been made: v1.01
 *      Added support for file types other than avi in/m4v out which was the only types supported by the original.
 *          These include the following: mkv, mp4, m4v, flv and ts
 *      Added ability to remember previous settings automatically: Input/Output folder locations, Handbrake CLI program
 *          location, the handbrake query string, and the check state of the "include sub directory check box".
 *      Added use of windows file selection dialog to select folder locations because it is easier to navigate.  Previously
 *          used folder selection dialogs.
 *      User no longer has to manually remove the -i and -o sections of the query generated by the hanbrake gui.
 *      User no longer has to remove a trailing "\" from the destination directory if it is present.
 *      Added some basic error handling for input/output paths but still needs more work.  see "To Do" below.
 *      
 *    The following changes have been made: v2.00
 * 
 *      Migrated the projec tto Visual Studio C# Express 2010.
 *      Added verify files/folders before running the CLI (verify the input is good before passing this to the handbrake cli).
 *      Added the ability to cancel and or pause encoding in-between individual files.
 *      Added delete key event handler for list of video files
 *      Added the ability to select from the HandBrake presets
 *      Added Create / Deletion of custom presets
 *      Added a Clone option for presets
 *      Added the ability to save the output in the same directory as the input source
 *      Added a new tab to show te output from the application as it is processing and disabled the shell dialog
 *      Added the ability to cancel all convertion tasks
 *      Added the ability to cancel a running convertion including the tidy up of the created file
 *      Added a free icon to the application
 *      Added Encode File drag drop
 *      Added the file being transferred to the status information
 *      Fixed For multiple selection in source folder, up and down operation is disabled.
 *
 *    The following changes have been made: v2.10
 *           Add file count in source dialog
 *           Added MTS and M2TS File input formats
 *           Allow supported types to be loaded externally by the user in the properties config file
 *           Added a Message box to warn the user that the User Presets could not be loaded.
 *           Added a log file using Log4Net to the application
 *           Added the ability to merge the processed file into a single MP4 file with filename Output.mp4
 *           Added a clear option on the Results view
 *
 *     The following changes have been made: v2.20
 *           Added a Menu Bar to the application
 *           Added Localization for German and English with an option to select via the menu (restart required )
 *           Added an About Dialog
 *           Added Exit to main menu bar
 *      */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Resources;
using System.Threading;
using System.Windows.Forms;
using HandBrake_Encoder.Properties;
using log4net;

namespace HandBrake_Encoder
{
    public partial class handBrakeBatchEncoder : Form
    {
        #region Fields and Properties
        private readonly System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(handBrakeBatchEncoder));
        private readonly ToolTip encodeButtonToolTip = new ToolTip();
        private readonly ToolTip encodeLocationButtonToolTip = new ToolTip();
        private readonly ToolTip encodeLocationTextBoxToolTip = new ToolTip();
        private readonly ToolTip fileRemoveButtonToolTip = new ToolTip();
        private readonly ToolTip folderRecursiveCheckBoxToolTip = new ToolTip();
        private readonly ToolTip folderSelectButtonToolTip = new ToolTip();
        private readonly ToolTip handBrakeCLILocationTextBoxToolTip = new ToolTip();
        private readonly ToolTip handBrakeCMDTextBoxToolTip = new ToolTip();
        private string destination;
        private bool executionCancelled;
        private string startBatchOperationText;
        private string cancelOperationText;
        private string handBrakeCLI;
        private string handBrakeCMD;
        private bool isShown;
        private Presets presets;
        private Process process;
        private readonly List<String> fileTypes = new List<String>();
        private readonly List<String> filesToMerge = new List<String>();
        public static ILog Log = LogManager.GetLogger("HandbrakeEncoder");
        private delegate void RemoveListItemCallBack();
        private ApplicationState appState =ApplicationState.Idle;
        private bool mergeOnlyOperation;
        
        public enum ApplicationState
        {
            Idle,
            Encoding,
            Merging,
            Complete
        }

        #endregion

        #region Construction Operations

        public handBrakeBatchEncoder()
        {
            // Assign the curent language
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.Language);
            
            InitializeComponent();
            
            LocalGUIInitialisation();
        }

        /// <summary>
        /// Initialises all of the GUI local settings
        /// </summary>
        private void LocalGUIInitialisation()
        {
            // Setup Log4Net Logging     
            log4net.Config.XmlConfigurator.Configure();

            // Use Error Level to ensure that this banner info is written into thelog file when it starts up everytime
            Log.Error("");
            Log.Error("");
            Log.Error("");
            Log.Error("**********************************************");
            Log.Error("Handbrake Batch Application");
            Log.Error("Ver. : " + Environment.Version);
            Log.Error("OS Version : " + Environment.OSVersion);
            Log.Error("**********************************************");
            Log.Error("");

            Log.Debug("Local Initialisation Successful " + DateTime.Now);
        
            folderRecursiveCheckBox.Checked = Settings.Default.IncludeSubs;
            handBrakeCLI = Settings.Default.HandBrakeCLILocation;
            handBrakeCMD = Settings.Default.HandBrakeCMD;
            folderBrowserDialog.RootFolder = Environment.SpecialFolder.MyComputer;
            folderBrowserDialog.ShowNewFolderButton = false;

            encodeFolderBrowserDialog.RootFolder = Environment.SpecialFolder.MyComputer;
            encodeFolderBrowserDialog.ShowNewFolderButton = false;

            encodeLocationTextBox.Text = Settings.Default.DefaultEncodeLocation;
            folderTextBox.Text = Settings.Default.FolderToEncode;

            handBrakeCLIOpenFileDialog.InitialDirectory = Environment.SpecialFolder.ProgramFiles.ToString();
            handBrakeCLIOpenFileDialog.Filter = "HandBrakeCLI|Handbrakecli.exe";
            handBrakeCMDTextBox.Text = handBrakeCMD;
            handBrakeCLILocationTextBox.Text = handBrakeCLI;
 
            if (Settings.Default.Language == "de-CH")
            {
                germanToolStripMenuItem.Checked = true;
                englishToolStripMenuItem.Checked = false;
            }else
            {
                germanToolStripMenuItem.Checked = false;
                englishToolStripMenuItem.Checked = true;
            }

            // Need to Fix This Issue
     //       startBatchOperationText = resources.GetString("StartBatchOperationText");
     //       cancelOperationText = resources.GetString("CancelBatchOperationText");
            startBatchOperationText = "Start Batch Operation";
            cancelOperationText = "Cancel Batch Operation";
        
            encodeButton.Text = startBatchOperationText;

            // Initialise the supported file types from the application config file
            LoadSupportedFileTypes();

            // Setup the last encode folder content as the source for the next configuration
            SetupSourceFolder(Settings.Default.FolderToEncode);
        }

 
        #endregion

        #region Helper Methods

        private void LoadSupportedFileTypes()
        {
            Log.Debug("Loading File Types");
            var values = new List<string>(Settings.Default.SupportedFileTypes.Split(' '));

            foreach (string val in values)
            {
                fileTypes.Add("*." + val);
            }
            Log.Debug(fileTypes.Count.ToString(CultureInfo.InvariantCulture) + " File Types Loaded");
        }

        private void encodeFromFolderEntered()
        {
            if (Directory.Exists(folderTextBox.Text))
            {
                Settings.Default.FolderToEncode = folderTextBox.Text;
                Settings.Default.Save();

                folderFileListBox.Items.Clear();
                foreach (string filetype in fileTypes)
                {
                    foreach (string f in Directory.GetFiles(folderTextBox.Text, filetype))
                    {
                        folderFileListBox.Items.Add(f);
                    }
                }
                DirSearch(folderTextBox.Text);
            }
            else
            {
                if (folderTextBox.Text != "")
                {
                    MessageBox.Show("This is not a valid path: " + folderTextBox.Text, "Error", MessageBoxButtons.OK,
                                    MessageBoxIcon.Exclamation);
                    folderTextBox.Focus();
                    folderTextBox.Select(0, folderTextBox.Text.Length);
                }
            }
        }

        private void encodeToLocationEntered()
        {
            if (Directory.Exists(encodeLocationTextBox.Text))
            {
                // Remove trailing \ if present
                string backslashtrailing = encodeLocationTextBox.Text.Substring(encodeLocationTextBox.Text.Length - 1);
                if (backslashtrailing == "\\")
                {
                    encodeLocationTextBox.Text = encodeLocationTextBox.Text.Substring(0,
                                                                                      encodeLocationTextBox.Text.Length -
                                                                                      1);
                }

                // Store results
                Settings.Default.DefaultEncodeLocation = encodeLocationTextBox.Text;
                Settings.Default.Save();
            }
            else
            {
                if (encodeLocationTextBox.Text != "")
                {
                    MessageBox.Show("This is not a valid path: " + encodeLocationTextBox.Text, "Error",
                                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    encodeLocationTextBox.Focus();
                    encodeLocationTextBox.Select(0, encodeLocationTextBox.Text.Length);
                }
            }
        }

        public void setPresets()
        {
            LoadPresetsDataSource();
        }

        private void DirSearch(string sDir)
        {
            try
            {
                if (folderRecursiveCheckBox.Checked)
                {
                    foreach (string d in Directory.GetDirectories(sDir))
                    {
                        foreach (string filetype in fileTypes)
                        {
                            foreach (string f in Directory.GetFiles(d, filetype))
                            {
                                folderFileListBox.Items.Add(f);
                            }
                        }

                        DirSearch(d);
                    }
                }
            }
            catch (Exception excpt)
            {
                Console.WriteLine(excpt.Message);
            }
        }

        private void RemoveListItem()
        {
            folderFileListBox.Items.RemoveAt(0);
            fileCountLabel.Text = folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture);
        }

        private bool validateDestSourceLocations()
        {
            bool status = true;

            if (folderFileListBox.Items.Count <= 0)
            {
                status = false;
            }
            else
            {
                // Validate Source Locations
                if (folderFileListBox.Items.Cast<string>().Any(file => !File.Exists(file)))
                {
                    status = false;
                }
            }

            // Validate Destination Locations
            if (status)
            {
                if (String.IsNullOrEmpty(encodeLocationTextBox.Text))
                {
                    status = false;
                }
                else
                {
                    if (!Directory.Exists(encodeLocationTextBox.Text))
                    {
                        status = false;
                    }
                }
            }

            return status;
        }

        private void DeleteSelectedSourceFiles()
        {
            if (folderFileListBox.SelectedIndices.Count > 0)
            {
                Log.Debug("Delete Operation Started");
                var itemList = new ArrayList();
                itemList.AddRange(folderFileListBox.SelectedItems);

                foreach (object item in itemList)
                {
                    folderFileListBox.Items.Remove(item);
                }

                Log.Debug(folderFileListBox.SelectedIndices.Count.ToString(CultureInfo.InvariantCulture) + " items deleted");
                fileCountLabel.Text = folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture);
            }
        }

        private void RemoveInputOutputDir()
        {
            string handbrake = handBrakeCMDTextBox.Text;
            int Index2 = 0;
            // Remove the Input Directory
            int Index1 = handbrake.IndexOf("\"");
            Index2 = handbrake.IndexOf("\"", Index1 + 1);
            if (Index1 != -1 && Index2 != -1)
            {
                handbrake = handbrake.Remove(Index1, (Index2 - Index1) + 1);
            }

            // Remove the Output Directory
            Index1 = handbrake.IndexOf("\"");
            Index2 = handbrake.IndexOf("\"", Index1 + 1);
            if (Index1 != -1 && Index2 != -1)
            {
                handbrake = handbrake.Remove(Index1, (Index2 - Index1) + 1);
            }

            // Update text box
            handBrakeCMDTextBox.Text = handbrake;
        }

        private void initHandbrakePresets()
        {
            Log.Debug("The Handbrake Presets are being Initialized");
            encodeLocationCheckBox.Checked = Settings.Default.UseSourceLocationForOutput;
            LoadPresetsDataSource();
        }

        private void LoadPresetsDataSource()
        {
            presets = new Presets();

            if (presets.GetMergedPresetList().Any())
            {
                try
                {
                    var bs = new BindingSource {DataSource = presets.GetMergedPresetList()};
                    handbrakePresetsComboBox.DataSource = bs.DataSource;
                    handbrakePresetsComboBox.DisplayMember = "Name";
                    handbrakePresetsComboBox.ValueMember = "Name";
                    handbrakePresetsComboBox.SelectedIndex = Settings.Default.SelectedPreset;
                }
                catch (ArgumentOutOfRangeException)
                {
                    handbrakePresetsComboBox.SelectedIndex = 0;
                }
                Log.Debug(handbrakePresetsComboBox.Items.Count + " Presets were loaded");
            }
            else
            {
                Log.Error("Unable to load the Handbrake Preset Information");
                MessageBox.Show("Unable to locate the Handbrake Preset information. Try starting the Handbrake application to ensure the Defaults presets have been created ");
            }
        }


		private void StartEncoding()
		{
			if(mergeOnlyOperation)
            {
                MergeOnlyClickOperation();
            } else
            {          
                EncodeAndMergeClickOperation();
            }
		}

		

        #endregion

        #region Events

        private void folderSelectButton_Click(object sender, EventArgs e)
        {
            Log.Debug("User selected to enter files to be converted");

            mergeOutputCheckBox.Checked = false;
            FolderSelectFileDialog1.InitialDirectory = Settings.Default.FolderToEncode;
            FolderSelectFileDialog1.FileName = "All";
            DialogResult result = FolderSelectFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                SetupSourceFolder(Path.GetDirectoryName(FolderSelectFileDialog1.FileName));
                Log.Debug(folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture) + " File loaded to be converted");
            }
        }
        
        private void SetupSourceFolder(string directory)
        {
            folderFileListBox.Items.Clear();
            folderTextBox.Text = directory;
            Settings.Default.FolderToEncode = directory;
            Settings.Default.Save();
            
            try
            {
                foreach (string filetype in fileTypes)
                {
                    if (directory != null)
                    {
                        foreach (string f in Directory.GetFiles(directory, filetype))
                        {
                            folderFileListBox.Items.Add(f);
                        }
                    }
                }

                DirSearch(directory);

                fileCountLabel.Text = folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture);
                UpdateDestinationLocation();
        }
        catch (Exception ex)
        {
            Log.Error("Error Setting up the source directory : " + ex.Message);
        }

    }


        private void encodeLocationButton_Click(object sender, EventArgs e)
        {
            FolderSelectFileDialog1.InitialDirectory = Settings.Default.DefaultEncodeLocation;
            FolderSelectFileDialog1.FileName = "All";
            DialogResult result = FolderSelectFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                //encodeLocationTextBox.Text = encodeFolderBrowserDialog.SelectedPath;
                string CurrentFolder = Path.GetDirectoryName(FolderSelectFileDialog1.FileName);
                encodeLocationTextBox.Text = CurrentFolder;
                Settings.Default.DefaultEncodeLocation = CurrentFolder;
                Settings.Default.Save();
            }
        }


        private void handBrakeCLIButton_Click(object sender, EventArgs e)
        {
            DialogResult result = handBrakeCLIOpenFileDialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                handBrakeCLILocationTextBox.Text = handBrakeCLIOpenFileDialog.FileName;
                Settings.Default.HandBrakeCLILocation = handBrakeCLILocationTextBox.Text;
                Settings.Default.Save();
            }
        }


        private void encodeButton_Click(object sender, EventArgs e)
        {
            if(mergeOnlyOperation)
            {
                MergeOnlyClickOperation();
            } else
            {          
                EncodeAndMergeClickOperation();
            }
        }

        private void EncodeAndMergeClickOperation()
        {
            if (appState.Equals(ApplicationState.Idle) || appState.Equals(ApplicationState.Complete))
            {
                // Clear on start.
                filesToMerge.Clear();

                if (!File.Exists(handBrakeCLILocationTextBox.Text))
                {
                    MessageBox.Show("Unable to find the Handbrake application");
                    return;
                }

                Log.Debug("The Encode operation has been started");
                if (validateDestSourceLocations())
                {
                    executionCancelled = false;
                    handbrakeTabControl.SelectedTab = handbrakeTabControl.TabPages["conversionOutputTabPage"];
                    convertionResultsTextBox.Text = "";
                    encodeButton.Text = cancelOperationText;
                    encodeButton.BackColor = Color.Red;
                    appState = ApplicationState.Encoding;
                    encodeFiles();
                }
                else
                {
                    Log.Error("The Source or Destination locations validation check failed");
                    MessageBox.Show("Operation Aborted : Error with a Source or Destination Location");
                }
            }
            else
            {
                appState = ApplicationState.Complete;
                Log.Debug("Batch File convertion has terminated");
                executionCancelled = true;
                encodeButton.Text = startBatchOperationText;
                encodeButton.BackColor = Color.LightGreen;
                KillHandBrakeCLIProcess();
            }
        }

        private void MergeOnlyClickOperation()
        {
            Log.Debug("Merge only operation selected");
            if (appState.Equals(ApplicationState.Idle) || appState.Equals(ApplicationState.Complete))
            {
                if (validateDestSourceLocations())
                {
                    // Assign the files to be merged.   Only MP4 files to be merged
                    Log.Debug("Create the list of files to be merged");
                    setupMergeFileList();

                    if (filesToMerge.Count > 0)
                    {
                        Log.Debug("Merge Operation Started, " + filesToMerge.Count.ToString(CultureInfo.InvariantCulture) + " files to be merged");

                        // Setup Status and Conditions
                        handbrakeTabControl.SelectedTab = handbrakeTabControl.TabPages["conversionOutputTabPage"];
                        convertionResultsTextBox.Text = "";
                        encodeButton.Text = cancelOperationText;
                        encodeButton.BackColor = Color.Red;
                        appState = ApplicationState.Merging;

                        // Clear Merge list
                        folderFileListBox.Items.Clear();
                        fileCountLabel.Text = "0";

                        // Start Merge Operation
                        mergeFiles();
                    }
                    else
                    {
                        Log.Error("There are no files in the list to be merged, operation terminated");
                        MessageBox.Show("There are no MP4 files available to merge");
                    }
                }
                else
                {
                    Log.Error("The Source or Destination locations validation check failed");
                    MessageBox.Show("Operation Aborted : Error with a Source or Destination Location");
                }
   

         
            }
        }

        private void setupMergeFileList()
        {
            filesToMerge.Clear();
            foreach (string filename in folderFileListBox.Items.Cast<string>().Where(filename => filename.EndsWith(".mp4")))
            {
                filesToMerge.Add(filename);
            }
        }


        private void fileRemoveButton_Click(object sender, EventArgs e)
        {
            DeleteSelectedSourceFiles();
        }

        private void fileUpButton_Click(object sender, EventArgs e)
        {
            if (folderFileListBox.SelectedIndex == -1 || folderFileListBox.SelectedIndex == 0)
            {
                return;
            }

            if (folderFileListBox.SelectedIndices.Count > 1)
            {
                Log.Info("Move of multiple items not supported");
                MessageBox.Show("Move of multiple items not supported");
                return;
            }
     
            object @select = folderFileListBox.Items[folderFileListBox.SelectedIndex];
            object previous = folderFileListBox.Items[folderFileListBox.SelectedIndex - 1];
            object temp = @select;
            select = previous;
            previous = temp;
            folderFileListBox.Items[folderFileListBox.SelectedIndex] = select;
            folderFileListBox.Items[folderFileListBox.SelectedIndex - 1] = previous;

            int val = folderFileListBox.SelectedIndex - 1;
            folderFileListBox.ClearSelected();
            folderFileListBox.SelectedIndex = val;
            Log.Debug("User chose to move an item up in the Source view Box");
        }


        private void fileDownButton_Click(object sender, EventArgs e)
        {
            if (folderFileListBox.SelectedIndex == -1 ||
                folderFileListBox.SelectedIndex == folderFileListBox.Items.Count - 1)
            {
                return;
            }

            if (folderFileListBox.SelectedIndices.Count > 1)
            {
                Log.Info("Move of multiple items not supported");
                MessageBox.Show("Move of multiple items not supported");
                return;
            }

            object @select = folderFileListBox.Items[folderFileListBox.SelectedIndex];
            object next = folderFileListBox.Items[folderFileListBox.SelectedIndex + 1];
            object temp = @select;
            select = next;
            next = temp;
            folderFileListBox.Items[folderFileListBox.SelectedIndex] = select;
            folderFileListBox.Items[folderFileListBox.SelectedIndex + 1] = next;

            int val = folderFileListBox.SelectedIndex + 1;
            folderFileListBox.ClearSelected();
            folderFileListBox.SelectedIndex = val;
            Log.Debug("User chose to move an item down in the Source view Box");
        }

        private void encodeLocationTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter)
            {
                encodeToLocationEntered();
            }
        }

        private void handBrakeCMDTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                RemoveInputOutputDir();
            }
        }

        private void folderRecursiveCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            Settings.Default.IncludeSubs = folderRecursiveCheckBox.Checked;
            Settings.Default.Save();
        }

        private void folderTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case '\r':
                    // perform necessary action
                    e.Handled = true;
                    break;
            }
        }

        private void encodeLocationTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case '\r':
                    // perform necessary action
                    e.Handled = true;
                    break;
            }
        }

        private void handBrakeCMDTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case '\r':
                    // perform necessary action
                    e.Handled = true;
                    break;
            }
        }

        private void folderTextBox_Leave(object sender, EventArgs e)
        {
            encodeFromFolderEntered();
        }

        private void handBrakeBatchEncoder_Load(object sender, EventArgs e)
        {
            initHandbrakePresets();
        }

        private void handbrakePresetsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            var result = (Preset) handbrakePresetsComboBox.SelectedItem;

            if (isShown)
            {
                Settings.Default.SelectedPreset = handbrakePresetsComboBox.SelectedIndex;
                Settings.Default.Save();
            }

            if (result != null)
            {
                handBrakeCMDTextBox.Clear();
                handBrakeCMDTextBox.Text = result.Query;
                Log.Debug("User has selected the preset : " + result.Name);
            }
            else
            {
                handBrakeCMDTextBox.Clear();
                handBrakeCMDTextBox.Text = "Unable to access the requested Handbrake Presets";
            }

        }

        private void enclodeLocationCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateDestinationLocation();
            Settings.Default.UseSourceLocationForOutput = encodeLocationCheckBox.Checked;
            Settings.Default.Save();
        }

        private void UpdateDestinationLocation()
        {
            if (encodeLocationCheckBox.Checked)
            {
                if (folderFileListBox.Items.Count > 0)
                {
                    var fileInfo = new FileInfo((string) folderFileListBox.Items[0]);
                    encodeLocationTextBox.Text = fileInfo.DirectoryName;
                }

                encodeLocationButton.Enabled = false;
                encodeLocationTextBox.Enabled = false;
            }
            else
            {
                encodeLocationTextBox.Text = Settings.Default.DefaultEncodeLocation;
                encodeLocationButton.Enabled = true;
                encodeLocationTextBox.Enabled = true;
            }
        }

        private void folderFileListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter)
            {
                encodeToLocationEntered();
            }
            if (e.KeyCode == Keys.Delete)
            {
                DeleteSelectedSourceFiles();
            }
        }

        private void folderTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                encodeFromFolderEntered();
            }
        }

        private void handBrakeCMDTextBox_TextChanged(object sender, EventArgs e)
        {
            RemoveInputOutputDir();
            Settings.Default.HandBrakeCMD = handBrakeCMDTextBox.Text;
            Settings.Default.Save();
        }


        private void encodeLocationTextBox_Leave(object sender, EventArgs e)
        {
            encodeToLocationEntered();
        }

        private void OKButton_Click(object sender, EventArgs e)
        {
            Log.Debug("The user has selected to save the current preset");
            var persistPreset = new Preset();
            Presets.PresetType currentPresetType =
                presets.GetPresetType(((Preset) handbrakePresetsComboBox.SelectedItem).Name);

            if (currentPresetType.Equals(Presets.PresetType.Default))
            {
                var enterPresetNameDialog = new SavePreset();
                if (enterPresetNameDialog.ShowDialog() == DialogResult.OK)
                {
                    persistPreset.Name = enterPresetNameDialog.NewName;
                }
                else
                {
                    // User Cancelled the operation so just cancel this operation
                    return;
                }
            }
            else
            {
                persistPreset.Name = ((Preset) handbrakePresetsComboBox.SelectedItem).Name;
            }

            // Save the Preset as a custom Preset and refresh the Combobox and select new entry.
            // Save as a custom
            persistPreset.Query = handBrakeCMDTextBox.Text;
            presets.AddPreset(Presets.PresetType.Custom, persistPreset);

            // Assign to local dialog 
            setPresets();
            handbrakePresetsComboBox.SelectedIndex = presets.GetIndexInMergedPresetList(persistPreset.Name);

            presets.ExportPresets();
        }

        private void CloneButton_Click(object sender, EventArgs e)
        {
            Log.Debug("The user has selected to clone the current Preset");
            var clonedPreset = new Preset();
            var savePresetDialog = new SavePreset();
            if (savePresetDialog.ShowDialog() == DialogResult.OK)
            {
                clonedPreset.Name = savePresetDialog.NewName;
                clonedPreset.Query = handBrakeCMDTextBox.Text;

                presets.AddPreset(Presets.PresetType.Custom, clonedPreset);

                // Assign to local dialog 
                setPresets();
                handbrakePresetsComboBox.SelectedIndex = presets.GetIndexInMergedPresetList(clonedPreset.Name);

                presets.ExportPresets();
            }
        }

        private void deletePresetButton_Click(object sender, EventArgs e)
        {
            Presets.PresetType currentPresetType =
                presets.GetPresetType(((Preset) handbrakePresetsComboBox.SelectedItem).Name);
            if (currentPresetType.Equals(Presets.PresetType.Default))
            {
                Log.Info("It is only possible to delete Custom Presets");
                MessageBox.Show("It is only possible to delete Custom Presets");
                return;
            }

            presets.RemovePresetByName(Presets.PresetType.Custom, ((Preset) handbrakePresetsComboBox.SelectedItem).Name);

            setPresets();
            handbrakePresetsComboBox.SelectedIndex = 0;

            presets.ExportPresets();
        }

        private void handBrakeBatchEncoder_Shown(object sender, EventArgs e)
        {
            isShown = true;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Just in case it is still running
            KillHandBrakeCLIProcess();

            Close();
        }

        private void handBrakeBatchEncoder_FormClosing(object sender, FormClosingEventArgs e)
        {
            Log.Debug("Handbrake Batch is closing");
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            mergeOnlyOperation = mergeOutputCheckBox.Checked;
        
            if(mergeOnlyOperation)
            {
                List<string> tempList = new List<string>();
                foreach(string filename in folderFileListBox.Items)
                {
                    if ( filename.EndsWith(".mp4") && (!filename.Contains("MergedOutput.mp4")))
                    {
                        tempList.Add(filename);
                    }
                }

                folderFileListBox.Items.Clear();
                folderFileListBox.Items.AddRange(tempList.ToArray());
                fileCountLabel.Text = folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture);
            }
        }

        private void clearLogsButton_Click(object sender, EventArgs e)
        {
            convertionResultsTextBox.Clear();
        }

        private void englishToolStripMenuItem_Click(object sender, EventArgs e)
        {
            germanToolStripMenuItem.Checked = false;
            Settings.Default.Language = "";
            Settings.Default.Save();

            MessageBox.Show("This operation requires a restart");
        }

        private void germanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            englishToolStripMenuItem.Checked = false;
            Settings.Default.Language = "de-CH";
            Settings.Default.Save();
            MessageBox.Show("This operation requires a restart");
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var about = new AboutBox();
            about.ShowDialog();
        }


		private void scheduleEncodingToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			if (!File.Exists(handBrakeCLILocationTextBox.Text))
			{
				MessageBox.Show("Unable to find the Handbrake application.  Plese set the location" +
								" before scheduling an encode time.", "Can not find HandBrake", MessageBoxButtons.OK, MessageBoxIcon.Information);
				if (handBrakeCLILocationTextBox.Text == "") handBrakeCLILocationTextBox.Text = "Please set the Handbrake CLI Location";
				handBrakeCLILocationTextBox.SelectAll();
				
				return;
			}

			if (folderFileListBox.Items.Count < 1)
			{
				MessageBox.Show("Please select files to be encoded before scheduling an encode time.",
								"Empty File List.", MessageBoxButtons.OK, MessageBoxIcon.Information);

				return;
			}

			if (!validateDestSourceLocations())
			{
				Log.Error("The Source or Destination locations validation check failed when scheduling an encode time.");
				MessageBox.Show("Error with a Source or Destination Location" +
								" Ensure these locations are correct before scheduling an encode time.",
								"Locations Invalid", MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			}

			
			// Get the time to start from the user
			var scheduleForm = new FormScheduler();
			if (scheduleForm.ShowDialog() == DialogResult.Cancel) return;

			// If the user gave a time to start then open the timer form
			var runTimerForm = new FormRunOnTimer(scheduleForm.timeToStart);
			scheduleForm.Dispose(); // dispose of the previous form

			// Start Timing
			var runOnTimerForm = new FormRunOnTimer(scheduleForm.timeToStart);
			this.Hide();
			DialogResult = runOnTimerForm.ShowDialog();

			if (DialogResult == DialogResult.OK)
			{
				this.Show();
				StartEncoding();
			}
			else this.Show();
			
		}



        #endregion // End Events

        #region Merge output into a single file
        
        private void mergeFiles()
        {
            AppendTextInResultsBox(convertionResultsTextBox, "Merge feature is possible thanks to the user of the MP3Box Utility" + Environment.NewLine);
            AppendTextInResultsBox(convertionResultsTextBox, "The Merge operation has been started" + Environment.NewLine);

            var fileInfo = new FileInfo(filesToMerge.First());
            var outputFilename = fileInfo.DirectoryName + "\\MergedOutput.mp4";
            if(File.Exists(outputFilename))
            {
                File.Delete(outputFilename);
            } 

            string mergeCommand = createMergeCommand(outputFilename);
            executeMergeTask(Environment.CurrentDirectory + "\\MP4Box\\MP4Box.exe", mergeCommand);
        }


        private void executeMergeTask(string applicationName, string arguments)
        {
            Log.Debug("merge Command : " + applicationName + " " + arguments);
            appState = ApplicationState.Merging;
            var processStartInfo = new ProcessStartInfo
            {
                FileName = applicationName,
                Arguments = arguments,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true
            };

            process = Process.Start(processStartInfo);

            process.EnableRaisingEvents = true;
            process.Exited += mergeProcess_exited;
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.OutputDataReceived += InterProcOutputHandler;
     
            process.BeginOutputReadLine();
        }

        private void mergeProcess_exited(object sender, EventArgs e)
        {
            AppendTextInResultsBox(convertionResultsTextBox, "Conversion operation is complete" + Environment.NewLine);
            EncodeButtonStatus(encodeButton, startBatchOperationText);
            appState = ApplicationState.Complete;
        }

        private string createMergeCommand(string outputFilename)
        { 
            // Merge Command 
            string mergeCommand = filesToMerge.Aggregate(" -force-cat -nodrop", (current, s) => current + (" -cat " + s));

            // Determine the output location
            mergeCommand += " -new " + outputFilename;

            return mergeCommand;
        }

        #endregion

        #region Encode Operations

        private void encodeFiles()
        {
            Log.Debug("Enclode Operation in process, " + folderFileListBox.Items.Count.ToString(CultureInfo.InvariantCulture) + " files to transfer");
            if (folderFileListBox.Items.Count > 0)
            {
                Log.Debug("Converting file : " + folderFileListBox.Items[0]);
        
                Log.Debug("Merge to single output file status : " + mergeOutputCheckBox.Checked.ToString(CultureInfo.InvariantCulture));
          
                // Create Handbrake string
                string handbrake = CreateHandbrakeParameterString();

                // execute the Encode operation on a seperate thread  
                ExecuteEncodeTask(handbrake);
            }
            else
            {
                mergeFiles();
            }
        }

        private void EncodeButtonStatus(Button button, string text)
        {
            if (InvokeRequired)
            {
                Invoke((Action<Button, string>) EncodeButtonStatus, encodeButton, text);
            }
            else
            {
                encodeButton.Text = text;
                encodeButton.BackColor = (text.Equals(text)) ? Color.LightGreen : Color.Red; 
            }
        }

        private string CreateHandbrakeParameterString()
        {
            string source = folderFileListBox.Items[0].ToString();
            if (encodeLocationCheckBox.Checked)
            {
                destination = source;
            }
            else
            {
                destination = encodeLocationTextBox.Text +
                              source.Substring(source.LastIndexOf(@"\"), source.Length - source.LastIndexOf(@"\"));
            }

            if (folderFileListBox.InvokeRequired)
            {
                RemoveListItemCallBack d = RemoveListItem;
                Invoke(d);
            }
            else
            {
                RemoveListItem();
            }

      
            // Remove file extension from destination
            destination = destination.Substring(0, destination.LastIndexOf(@"."));

            // Get output file extension from the handbrake generated query
            int Index2 = 0;
            string OutputType = handBrakeCMDTextBox.Text;
            int Index1 = OutputType.IndexOf(@"-f ") + 3;
            Index2 = OutputType.IndexOf(@"-", Index1 + 1);

            // Handle -f being the last entry in the query
            if (Index2 == -1)
            {
                Index2 = OutputType.Length;
            }
            OutputType = "." + OutputType.Substring(Index1, Index2 - Index1);
            OutputType = OutputType.Replace(" ", "");

            // Add Output file type to the destination
            destination += OutputType;

            // Delete file it it already exists
            if (File.Exists(destination))
            {
                Log.Debug("Delete destination file if it exists");
                File.Delete(destination);
            }
                
            // Add file to merge file list for the second stage of the operation
            filesToMerge.Add(destination);
       
            // Add the input and output to the CMD
            string handbrake = handBrakeCMDTextBox.Text;
            Index1 = handbrake.IndexOf(@"-i") + 2;
            Index2 = handbrake.IndexOf(@"-o") + 2;

            handbrake = " -i " + '\u0022' + source + '\u0022';
            handbrake += " -o " + '\u0022' + destination + '\u0022';

            handbrake += " " + handBrakeCMDTextBox.Text;
            return handbrake;
        }

      
        private void ExecuteEncodeTask(string handbrake)
        {
            Log.Debug("handbrake Command : " + handBrakeCLILocationTextBox.Text + " " + handbrake);
            var processStartInfo = new ProcessStartInfo
                                       {
                                           FileName = handBrakeCLILocationTextBox.Text.Trim(),
                                           Arguments = handbrake,
                                           UseShellExecute = false,
                                           CreateNoWindow = true,
                                           RedirectStandardOutput = true
                                       };

            process = Process.Start(processStartInfo);

            process.EnableRaisingEvents = true;
            process.Exited += process_exited;
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.OutputDataReceived += InterProcOutputHandler;

            process.BeginOutputReadLine();
        }

        private void process_exited(object sender, EventArgs e)
        {
            if (!executionCancelled)
            {
                encodeFiles();
            }
        }

        private void AppendTextInResultsBox(TextBox box, string text)
        {

            if (InvokeRequired)
            {
                Invoke((Action<TextBox, string>) AppendTextInResultsBox, convertionResultsTextBox, text);
            }
            else
            {
                box.AppendText(DateTime.Now.ToLocalTime().ToLongTimeString() + " : " + text);
            }
        }

        private void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
        {
            AppendTextInResultsBox(convertionResultsTextBox, destination + " : " + outLine.Data + Environment.NewLine);
        }

        private void KillHandBrakeCLIProcess()
        {
            Process[] runningProcesses = Process.GetProcesses();

            foreach (Process t in runningProcesses)
            {
                if (t.ProcessName == "HandBrakeCLI")
                {
                    t.Kill();
                }
            }

            AppendTextInResultsBox(convertionResultsTextBox, "Operation Cancelled By User" + Environment.NewLine);

            // It can take some time for the process to die and unlock the new file
            for (int deleteAttempts = 0; deleteAttempts < 5; deleteAttempts++)
            {
                try
                {
                    if (File.Exists(destination))
                    {
                        File.Delete(destination);
                        Thread.Sleep(1000);
                        break;
                    }
                }
                catch (Exception)
                {
                    Thread.Sleep(1000);
                }
            }
        }

        #endregion

        #region Drag and Drop

        private void folderFileListBox_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void folderFileListBox_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                var filenames = (string[]) e.Data.GetData(DataFormats.FileDrop);
                var onlyFiles = new ArrayList();

                foreach (string filename in filenames)
                {
                    if (File.Exists(filename))
                    {
                        onlyFiles.Add(filename);
                    }
                }
                folderFileListBox.Items.AddRange(onlyFiles.ToArray());
            }
        }

        #endregion










	}
}